/***
*ptr.h
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:   COM Wrapper for the Managed World
*
*       [Public]
*
****/

#pragma once

#if !defined(_INC_MSCLR_COM_PTR)

#ifndef __cplusplus_cli
#error ERROR: msclr libraries are not compatible with /clr:oldSyntax
#endif

#include <vcclr.h>

#include <msclr\safebool.h>

namespace msclr
{

namespace _detail
{
    
    // COM::ptr::operator->() returns a smart_com_ptr instead of
    // a regular interface pointer so that a Release is done automatically
    // when the temporary is destroyed
    
    template<class _interface_type>
    ref class smart_com_ptr
    {
        public:
        smart_com_ptr(_interface_type * p)
        {
            ptr= p;
        }

        smart_com_ptr(const smart_com_ptr % ip)
        {
            ptr = ip.ptr;
            ptr->AddRef();
        }

        _interface_type * operator ->()
        {
            return ptr;
        }

        ~smart_com_ptr()
        {
            ptr->Release();
        }
        
        private:
        _interface_type * ptr;        
    };

} //namespace _detail


namespace com
{

    template<class _interface_type>
    ref class ptr
    {
        public:

        ptr():obj_rcw(nullptr)
        {

        }

        // Construct from interface pointer
        ptr(_interface_type * p)
        {
            obj_rcw = nullptr;
            assign(p);
        }

        // Attach to an interface pointer
        void Attach(_interface_type * _right)
        {
            if (valid()) 
            {
                throw gcnew System::InvalidOperationException("COM::ptr.Attach");
            }

            assign(_right);
        }

        // Assign an interface pointer
        ptr<_interface_type> % operator=(_interface_type * _right)
        {
            Attach(_right);
            return *this;
        }

        // All CreateInstance methods create an instance of a COM Object 
        // by calling CoCreateInstance
        void CreateInstance(System::String ^ prog_id, LPUNKNOWN pouter, DWORD cls_context)
        {
            wchar_t * pwszprog_id = NULL;
            
            if(prog_id != nullptr)
            {
                pin_ptr<const __wchar_t> _pinned_ptr = PtrToStringChars( prog_id );
                pwszprog_id = _wcsdup(_pinned_ptr);
            }

            try 
            {
                CreateInstance(pwszprog_id, pouter, cls_context);
            }
            finally 
            {
                free(pwszprog_id);
            }
        }

        void CreateInstance(System::String ^ prog_id, LPUNKNOWN pouter)
        {
            CreateInstance(prog_id, pouter, CLSCTX_ALL);
        }

        void CreateInstance(System::String ^ prog_id)
        {
            CreateInstance(prog_id, NULL, CLSCTX_ALL);
        }

        void CreateInstance(const wchar_t* progid, LPUNKNOWN pouter, DWORD cls_context)
        {
            CLSID clsid;
            System::Runtime::InteropServices::Marshal::ThrowExceptionForHR(
                CLSIDFromProgID(progid, &clsid));
            CreateInstance(clsid, pouter, cls_context);
        }

        void CreateInstance(const wchar_t * progid, LPUNKNOWN pouter)
        {
            CreateInstance(progid, pouter, CLSCTX_ALL);
        }

        void CreateInstance(const wchar_t * progid)
        {
            CreateInstance(progid, NULL, CLSCTX_ALL);
        }

        void CreateInstance(REFCLSID rclsid,LPUNKNOWN pouter,DWORD cls_context)
        {
            if (valid()) 
            {
                throw gcnew System::InvalidOperationException("COM::ptr.CreateInstance");
            }

            _interface_type * interface_ptr = NULL;
            
            System::Runtime::InteropServices::Marshal::ThrowExceptionForHR(CoCreateInstance(
                        rclsid, pouter, cls_context, __uuidof(_interface_type),
                        (void**)&interface_ptr));

            if (interface_ptr) 
            {
                assign(interface_ptr);
                interface_ptr->Release();
            }
        }

        void C